package com.kusiri.kframework.component
{
	import com.kusiri.kframework.KfContext;
	
	import flash.utils.describeType;
	
	import mx.controls.Alert;
	import mx.logging.ILogger;
	import mx.logging.Log;
	import mx.managers.CursorManager;
	import mx.utils.UIDUtil;
	
	/**
	 * Manages the initialisation, and dependencies, of components
	 * TODO: Add timeout to component init...
	 */
	public class KfComponentManager
	{
		protected var kfModule : KfContext;
		
		/**
		 * Logger
		 */
		protected var logger:ILogger = Log.getLogger('com.kusiri.kframework.KfComponentManager');
		
		private var isInitialized : Object = {};
		private var isInitializing : Object = {};
		private var failedToInitialize : Object = {};
		
		private var onSuccessCallbacks : Object = {};
		private var onFailCallbacks : Object = {};

		public function KfComponentManager( kfModule : KfContext ) : void
		{
			this.kfModule = kfModule;
		}

		/**
		 * Tell the UI Component to load its data.
		 */
		public function initializeComponent(component : IKfComponent, onInitialized : Function, onFail : Function) : void
		{
			
			
			
			logger.debug('Initialising '+(component as Object).toString());
			
			var uid:String = UIDUtil.getUID(component);
			
			if (isInitialized[uid])
			{
				logger.debug('Bailing: already initialised');
				onInitialized(component);
				return;
			}
			
			if (failedToInitialize[uid])
			{
				logger.debug('Bailing: failed to initialise');
				onFail(component);
				return;
			}
			
			kfModule.waitingForSomething();
			
			onSuccessCallbacks[uid]||=[];
			onSuccessCallbacks[uid].push(onInitialized);
			onFailCallbacks[uid]||=[];
			onFailCallbacks[uid].push(onFail);
			
			if (  isInitializing[uid] )
			{
				logger.debug('Bailing: already initialising - added to call stack');
			}

			isInitializing[uid] = true;
			
			
			var kfDependencyComponents : Array = [];
			
			// simple for..in loop doesn't work
			
            var classInfo:XML = describeType(component);

            // List the object's variables, their values, and their types.
            for each (var v:XML in classInfo..variable) 
            {
 				_extractComponents(component, component[v.@name], kfDependencyComponents);
            }

//            // List accessors as properties.
//            for each (var a:XML in classInfo..accessor) {
//            	if (a.@access != 'writeonly') {
//					_extractComponents(component[a.@name], kfDependencyComponents);
//            	}
//            } 
			
			logger.debug('Got '+kfDependencyComponents.length+' components to recursively initialise');
			
			// no dependencies?
			if (kfDependencyComponents.length == 0)
			{
				component.__initializeComponent(__initializationFinished, __initializationFailed);
				return;
			}
			
			var uninitializedInitialComponentCount:int = kfDependencyComponents.length;
			for each ( var c: IKfComponent in kfDependencyComponents)
			{
				logger.debug('Recursively initialising '+(c as Object).toString());
				initializeComponent(c, 
					function(c:IKfComponent):void
					{
						logger.debug('Recursively initialised '+(c as Object).toString());
						uninitializedInitialComponentCount--;
						if (uninitializedInitialComponentCount == 0)
						{
							component.__initializeComponent(__initializationFinished, __initializationFailed);
						}
					}
					,
					function(c:IKfComponent, error:*):void
					{
						isInitializing[uid] = false;
						isInitialized[uid] = false;
						kfModule.finishedWaitingForSomething();
						Alert.show('Could not initialise component.');
						// app will remain disabled now, as uninitializedInitialComponentCount > 0
					}
				);
			}
			
		}

		/**
		 * Not called directly.
		 * Callback invoked by the component if init fails
		 */
		public function __initializationFailed(component:IKfComponent, error:*=null) : void
		{
			if (component==null)
			{
				throw Error('Null component');
			}
			
			// remove any cursors
			CursorManager.removeAllCursors();			
			
			logger.debug('Initialisation failed for '+component);
			
			var uid:String = UIDUtil.getUID(component);
			
			failedToInitialize[uid] = true;
			isInitializing[uid] = false;
			
			// we've succeeded by design here
			for each ( var fn : Function in onFailCallbacks[uid])
			{
				fn(component, error);
			}
			
			delete onSuccessCallbacks[uid];
			delete onFailCallbacks[uid];
		}
		
		/**
		 * Not called directly.
		 * Callback invoked by the component if init succeeds
		 */
		public function __initializationFinished(component:IKfComponent) : void
		{
			if (component==null)
			{
				throw Error('Null component');
			}
			
			kfModule.finishedWaitingForSomething();
			
			logger.debug('Initialisation finished for '+component);
			
			var uid:String = UIDUtil.getUID(component);
			
			isInitialized[uid] = true;
			isInitializing[uid] = false;
			
			// we've succeeded by design here
			for each ( var fn : Function in onSuccessCallbacks[uid])
			{
				fn(component);
			}
			
			delete onSuccessCallbacks[uid];
			delete onFailCallbacks[uid];
		}

		private function _extractComponents(component:IKfComponent, object:*, kfDependencyComponents:Array) : void
		{
			if (object === component) return;
			
            if (object is KfComponentCollection) 
            {
            	object.source.forEach( function(element:*, index:int, arr:Array):void { kfDependencyComponents.push(element); } );
            }
			else if (object is IKfComponent) 
			{
				kfDependencyComponents.push(object);
			}
		}

	}
}